﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Aquasoft.Iszr.Proxy.WebServices;
using Aquasoft.Iszr.Proxy.WebServices.Interfaces;

namespace Aquasoft.Iszr.Proxy.ZadostInfoUtils
{
    public static class IszrZadostInfoManager
    {
        #region STATIC Current

        private static object __locker = new object();

        [ThreadStatic]
        private static IszrZadostInfoScopeBase __currentScope;
        public static IszrZadostInfoScopeBase CurrentScope
        {
            get { return __currentScope; }
        }

        #endregion

        private static bool __isRegistered = false;
        private static bool __isAutoSetByScopeEnabled = false;
        private static bool __isAutoSetByAnonymousEnabled = false;
        private static Func<string> __getIszrUzivatel;
        private static Func<ISzrZadostInfo, ZadostInfoType> __getAnonymousZadostInfoFunc;

        public static bool IsAutoSetByScopeEnabled
        {
            get { return __isAutoSetByScopeEnabled; }
        }

        public static bool IsAutoSetByAnonymousEnabled
        {
            get { return __isAutoSetByAnonymousEnabled; }
        }

        /// <summary>
        /// Získá login aktuáně přihlášeného uživatele pro volání ISZR.
        /// V případě, že dohledaný login je prázný nebo moc dlouhý (>100), je vrácena vyjímka.
        /// </summary>
        /// <returns>Login uživatele pro ISZR</returns>
        public static string GetCurrentIszrUzivatel()
        { 
            var getIszrUzivatel = __getIszrUzivatel;
            if (getIszrUzivatel == null)
                throw new InvalidOperationException("Nelze získat identitu aktuálně přihlášeného uživatele. Je nutné aktivovat funkci IszrZadostInfoManager.EnableAutoSetIszrZadostInfoByScope.");

            string login = getIszrUzivatel();
            if (string.IsNullOrWhiteSpace(login))
                throw new InvalidOperationException("Nelze zjistit identitu aktuálně přihlášeného uživatele.");
            if (login.Length > 100)
                throw new InvalidOperationException("Identitu aktuálně přihlášeného uživatele je příliš dlouhá.");

            return login;
        }

        /// <summary>
        /// Umožní automatické vyplnění ZadostInfo dle údajů z <see cref="IszrZadostInfoScopeBase"/>.
        /// </summary>
        /// <param name="getIszrUzivatel">Funkce, která na základě aktuálně přihlášeného uživatele vrátí login pro ISZR</param>
        public static void EnableAutoSetIszrZadostInfoByScope(Func<string> getIszrUzivatel, IszrZadostInfoData defaultZadostInfoData = null)
        {
            if (__isAutoSetByScopeEnabled)
                throw new InvalidOperationException("AutoSetIszrZadostInfoByScope already enabled.");
            if (getIszrUzivatel == null)
                throw new ArgumentNullException("getIszrUzivatel");

            EnsureIsCallEventRegistered();
            if (defaultZadostInfoData != null)
                IszrZadostInfoScope.SetDefaultData(defaultZadostInfoData);
            __getIszrUzivatel = getIszrUzivatel;
            __isAutoSetByScopeEnabled = true;
        }

        /// <summary>
        /// Umožní nastavit ZadostInfo v případě, že je ISZR voláno mimo <see cref="IszrZadostInfoScopeBase"/>.
        /// </summary>
        /// <param name="getAnonymousZadostInfoFunc">Funkce vytvářející ZadostInfo</param>
        public static void EnableAnonymousZadostInfo(Func<ISzrZadostInfo, ZadostInfoType> getAnonymousZadostInfoFunc)
        {
            if (__isAutoSetByAnonymousEnabled)
                throw new InvalidOperationException("AnonymousZadostInfo already enabled.");
            if (getAnonymousZadostInfoFunc == null)
                throw new ArgumentNullException("getAnonymousZadostInfoFunc");

            EnsureIsCallEventRegistered();
            __getAnonymousZadostInfoFunc = getAnonymousZadostInfoFunc;
            __isAutoSetByAnonymousEnabled = true;
        }

        /// <summary>
        /// Registruje nově vytvořený scope
        /// </summary>
        /// <param name="scope"></param>
        /// <returns>Parent scope (or null)</returns>
        internal static IszrZadostInfoScopeBase RegisterScope(IszrZadostInfoScopeBase scope)
        {
            IszrZadostInfoScopeBase parent;
            lock(__locker)
            {
                parent = __currentScope;
                __currentScope = scope;
            }

            return parent;
        }

        private static void EnsureIsCallEventRegistered()
        {
            if (__isRegistered)
                return;

            lock (__locker)
            {
                if (__isRegistered)
                    return;
                
                var serviceManagerCallEvents = IszrServiceManager.Instance as IIszrServiceManagerCallEvents;
                if (serviceManagerCallEvents == null)
                    throw new ApplicationException("IszrServiceManager must implement IIszrServiceManagerCallEvents.");

                serviceManagerCallEvents.BeforeCallService += serviceManagerCallEvents_BeforeCallService;
                __isRegistered = true;
            }
        }

        private static void serviceManagerCallEvents_BeforeCallService(IszrServiceBase service, object request)
        {
            var requestWithZadostInfo = request as ISzrZadostInfo;
            if (requestWithZadostInfo != null)
            {
                if (__isAutoSetByScopeEnabled && requestWithZadostInfo.ZadostInfo == null)
                {
                    var scope = __currentScope;
                    if (scope != null)
                        requestWithZadostInfo.ZadostInfo = scope.GetZadostInfo(requestWithZadostInfo);
                }

                if (__isAutoSetByAnonymousEnabled && requestWithZadostInfo.ZadostInfo == null)
                {
                    var getDefaultZadostInfoFunc = __getAnonymousZadostInfoFunc;
                    if (getDefaultZadostInfoFunc != null)
                        requestWithZadostInfo.ZadostInfo = getDefaultZadostInfoFunc(requestWithZadostInfo);
                }
            }
        }
    }
}
